#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include<omp.h>
#include<ctype.h>

#define NTHREADS 4
#define W_SIZE 1000
#define N_MAX 3000
#define N_PRIMES 367


/*
 * Vector with all the prime numbers from 2 to 2500
 * */
int PrimeNumbers[N_PRIMES];

/*
 * Verifica se um caracter eh um dos caracteres que
 * delimitam frases
 * @ ENTRADA
 *   letter - caracter que se quer verificar
 * @ SAIDA
 *   int - 1 se o caracter for um delimitador de frase,
 *         e 0 caso contrario
 */
int isEndOfPhrase(char c);

/*
 * Encontra as posicoes do vetor com o texto dentro das quais
 * cada thread vai atuar
 * @ ENTRADA
 *   *text - vetor de caracter com o texto analisado
 *   fileSize - tamanho do vetor de texto
 *   nThreads - numero de threads
 * @ SAIDA
 *   *cutoffs - vetor de inteiros com as posicoes que delimitam
 *              cada bloco
 */
void findCutoffs(int *cutoffs, char *text, long int fileSize, int nThreads);

/*
 * Function: ClearString
 * Params: char *String
 * Return: void
 * Clean the String received
 * */
void ClearString(char *String);

/*
 * Function: CrivosAlgorithm
 * Param: void
 * Return: void
 * Calculates all the prime numbers from 2 to 2500.
 * The numbers are available to be consulted in the global vector PrimeNumbers*/
void CrivosAlgorithm();

/*
 * Function: isPrime
 * Param: char *String
 * return: int : 1 - TRUE; 0 - FALSE
 * The function will sum the ascii values of the String and check if this number is in the PrimeNumbers vector.
 * If it is, the function return TRUE (1) else return FALSE (0)*/
int isPrime(char *String);

/*
 * Function: isPalindrome
 * Param: char *String
 * return: int: 1 - TRUE; 0 - FALSE
 * The function will check is the String is a Palindrome
 * */
int isPalindrome(char *String);

/*
 * Function: inTheWord
 * Param: char c
 * return int: 1 _ TRUE; 0 - FALSE
 * Verify if the char is in the word
*/
int inTheWord(char c);
/*
 * Function: CountOnFile
 * Param: FILE *f
 * Return: Int vector with the number of palindromes and primes
 *         [0] Number of Palindrome words
 *         [1] Number of Prime Numbers
 * The function count from the file f the number of Palindrome Words
 * and the number of words with ascii value equals a prime number*/
void CountOnFile(char *StringTemp, long int sizeFile, int Numbers[]);

int main(void){

  int        nPalindW = 0, nPrimes = 0;
  long int   fileSize;
  FILE       *f = fopen("wikipedia.txt", "r");
  char       *text;
  int        cutoffs[NTHREADS + 1];


  if(f == NULL) {
    printf("Nao foi possivel abrir o arquivo\n");
    exit(1);
  }
  /*
   * calcula o tamanho do arquivo de texto
   */
  fseek(f, 0, SEEK_END);
  fileSize = ftell(f);
  /* aloca um vetor de char para armazenar todo o arquivo
   * em memoria principal
   */
  text = (char*) malloc(fileSize + 1);
  if(text == NULL) {
    printf("Erro ao tentar alocar memoria para o texto\n");
    exit(1);
  }

  /*
   * retorna o cursor para o inicio do arquivo
   */
  rewind(f);
  /*
   * salva todo o arquivo no vetor 'block'
   */
  fread(text, sizeof(char), fileSize + 1, f);
  /*
   * encontra as posicoes do vetor 'block' dentro das quais cada
   * thread vai computar
   */
  findCutoffs(cutoffs , text, fileSize, NTHREADS);

  CrivosAlgorithm();

#pragma omp parallel num_threads(NTHREADS) firstprivate(text, cutoffs) reduction(+:nPalindW) reduction(+:nPrimes)
  {
    int begin, end;
    int n[2];
    int threadNumber;

    threadNumber = omp_get_thread_num(); /* obtem o nro da thread */
    begin = cutoffs[threadNumber]; /* obtem o inicio do bloco a ser analisado */
    end = cutoffs[threadNumber + 1]; /* obtem o fim do bloco a ser analisado */
    /*
     * calcula o nro de palindromos
     */
    CountOnFile(&text[begin], end - begin, n);
    printf("\n ------------------\n Thread %d\n ------------------\n Palindromes found:\n Word - %7d\n Phrases - %4d\n", threadNumber, n[0], n[1]);
    nPalindW = n[0];
    nPrimes = n[1];
  }
    printf("\n ---------------------------\n");
  printf("\nPalindromes found: %d\nPrimes found:       %d\n\n", nPalindW, nPrimes);

  return 0;
}

int isEndOfPhrase(char c){
    //if the char is one of this symbols, its an end of phrase
    if(c == '.' || c == ';' || c == '(' || c == ')' || c == '"' ||
       c == ':' || c == '<' || c == '>' || c == '{' || c == '}' ||
       c == '[' || c == ']' || c == '|' || c == '&' || c == '!' ||
       c == '?' || c == '\n'|| c == '\t'|| c == '\r' )
       return 1;
    else return 0;
}

void findCutoffs(int *cutoffs, char *text, long int fileSize, int nThreads){
  int blockSize;
  int i;

  /*
   * calcula o tamanho aproximado que os blocos de caracteres que cada tread
   * vai computar devem ter
   */
  blockSize = fileSize / nThreads;
  cutoffs[0] = 0; /* o inicio do 1o bloco eh sempre zero */
  /*
   * encontra, no texto, o proximo final de frase a partir
   * de cada ponto que deveria marcar o fim de bloco
   */
  for(i = 1; i < nThreads; i++) {
    /* recebe a posicao que deveria marcar o fim do i-esimo bloco */
    cutoffs[i] = i * blockSize;
    while( !isEndOfPhrase(text[ cutoffs[i] ]) )
      cutoffs[i]++;
    cutoffs[i]++;
  }
  /* o final do ultimo bloco sempre eh o fim do texto */
  cutoffs[i] = fileSize;
}

void ClearString(char *String){
    //Gets the size of the string and change every char for a '\0'
    int i, size = strlen(String);
    for(i=0; i< size; i++){
        String[i] = '\0';
    }
}

void CrivosAlgorithm(){
    // We create the local variables and set the initial values
    int auxVector[N_MAX];
    int check = sqrt(N_MAX);
    int i;
    int aux=0;
    int inc =0;

    //Fill the auxVector with numbers from 2 to N_MAX+2
    for(i=0; i<N_MAX; i++){
        auxVector[i] = i+2;
    }

    //Eliminates the non-prime numbers (multiples of i)
    for(i=0; i<check; i++){
        if(auxVector[i] != 0){
            inc = auxVector[i];
            aux = i+inc;

            while(aux < N_MAX){
                if(auxVector[aux] != 0){
                    auxVector[aux] = 0;
                }
                aux += inc;
            }
        }
    }

    //Fill the Global Vector
    aux=0;
    for(i=0; i<N_PRIMES; i++){
        if(auxVector[i] >0){
            PrimeNumbers[aux] = auxVector[i];
            aux++;
        }
    }
}

int isPrime(char *String){
    int sum = 0;
    int i;

    //Sum the ASCII values from string
    for(i=0; i<strlen(String); i++){
        sum += String[i];

    }
    //Verify if the sum is in the Global Vector
    for(i=0; i<N_PRIMES; i++){
        if(PrimeNumbers[i] > sum){
            break;
        }
        else if(PrimeNumbers[i] == sum){
            return 1;
        }
    }
    return 0;
}

int isPalindrome(char *String){
    // We create the local variables and set the initial values
    char inverse_String[W_SIZE];
    int count, size = strlen(String);
    int i;

    long v = String[0];

    // If the String isn't just a letter, i.e: 'a', verify if its a palindrome
    if(strlen(String) > 1 && v != '\0'){
        //transform all the letters to lower chars
        for(i=0; i<size; i++){
            String[i] = tolower(String[i]);
        }

        count = size-1;
        ClearString(inverse_String);
        //copy the string to a aux string in the inverse
        for(i=0; i<size; i++){
            inverse_String[i] = String[count];
            count--;
        }
        //compare both strings
        if(strcmp(inverse_String, String) == 0){
            return 1;
        }
        else return 0;
    }
    else return 0;
}

int inTheWord(char c){
    //if the char is one of this symbols, its not the final of the word
    if(((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')))
        return 1;

    else return 0;
}

void CountOnFile(char *StringTemp, long int sizeFile, int Numbers[]){
    // Create and Set the variables and its initials values
    char Word[W_SIZE];
    char c;

    int i;
    long int counter=0;

    ClearString(Word);

    Numbers[0] = 0;
    Numbers[1] = 0;

    // For each char of the file, analyses if indicates an end of word
    for(i=0; i<sizeFile; i++){
        c = StringTemp[i];
        //If it is a letter or number, continue the analysis
        if( inTheWord(c) ){
            Word[counter] = c;
            counter++;
        }
        else{
            //Verify if the word is a palindrome
            if(isPalindrome(Word) && strlen(Word) > 1){
	      Numbers[0]++;
	      //verify if the sum of the ascii values is a prime number
	      if(isPrime(Word) && strlen(Word)) {
		Numbers[1]++;
	      }
            }

            ClearString(Word);
            counter=0;
        }
    }
    ClearString(StringTemp);
}













